package com.cjh.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.access.AccessDeniedHandler;
import org.springframework.security.web.authentication.AuthenticationFailureHandler;

import javax.security.auth.login.CredentialException;
import javax.security.auth.login.CredentialExpiredException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Map;

/**
 * web安全配置类
 */
@Configuration
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

    /**
     * 配置认证管理
     * @param auth
     * @throws Exception
     */
    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
//        不要用他父类的东西
//        super.configure(auth);

        //在内存中创建了两个用户
        auth.inMemoryAuthentication()
                .withUser("admin")
                .password(passwordEncoder().encode("admin"))
                .roles("admin")//角色
                .authorities("sys:add","sys:delete","sys:update","sys:select")//权限，如果角色和权限都设置，则角色失效
                .and()
                .withUser("cjh")
                .password(passwordEncoder().encode("cjh"))
                .roles("test")
                .authorities("sys:select");
    }

    @Autowired
    private AuthenticateSuccess authenticateSuccess;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
//        super.configure(http);
        //给一个登录表单（非前后端分离）
//        http.formLogin()
//            .successForwardUrl("/welcome")//登录成功后请求的接口
//            .failureForwardUrl("/fail")//登录失败后请求的接口
//            .permitAll();//放行

        //登录成功或失败的执行处理器（前后端分离）
        http.formLogin()
                .successHandler(authenticateSuccess)
                .failureHandler(authenticationFailureHandler());
        http.exceptionHandling().accessDeniedHandler(accessDeniedHandler());

        //配置方式认证用户权限（一般使用控制器的注解）
//        http.authorizeRequests()
//                .antMatchers("/add").hasAuthority("sys:add")//访问/add接口必须要有sys:add权限
//                .antMatchers("/delete").hasAuthority("sys:delete")
//                .antMatchers("/update").hasAuthority("sys:update")
//                .antMatchers("/select").hasAuthority("sys:select")
//                .antMatchers("/role").hasRole("/test");
        //所有请求都要登录后才能访问
        http.authorizeRequests().anyRequest().authenticated();

    }

    /**
     * 自定义登录失败的处理器
     * 返回json
     * @return
     */
    @Bean
    public AuthenticationFailureHandler authenticationFailureHandler(){
        return (request, response, exception)->{
            response.setContentType("application/json;charset=utf-8");
            System.out.println(exception);
            Map<String,Object> map=new HashMap<>();
            map.put("code",401);
            //instanceof判断左边是否等于右边的一个实例
            if (exception instanceof LockedException){
                map.put("msg","账户被锁定");
            }else if (exception instanceof BadCredentialsException){
                map.put("msg","用户名或密码错误");
            }else if (exception instanceof DisabledException){
                map.put("msg","账户被禁用");
            }else if (exception instanceof AccountExpiredException){
                map.put("msg","账户已过期");
            }
//            else if (exception instanceof CredentialExpiredException){
//                map.put("msg","密码已过期");
//            }
            else {
                map.put("msg","登录失败");
            }
            ObjectMapper objectMapper=new ObjectMapper();
            String s = objectMapper.writeValueAsString(map);
            PrintWriter writer = response.getWriter();
            //把返回信息写出去返回给前端
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 自定义请求403的处理器
     * 返回json
     * @return
     */
    @Bean
    public AccessDeniedHandler accessDeniedHandler(){
        return (request, response, exception)->{
            response.setContentType("application/json;charset=utf-8");
            Map<String,Object> map=new HashMap<>();
            map.put("code",403);
            map.put("msg","您没有权限");
            ObjectMapper objectMapper=new ObjectMapper();
            String s = objectMapper.writeValueAsString(map);
            PrintWriter writer = response.getWriter();
            //把返回信息写出去返回给前端
            writer.write(s);
            writer.flush();
            writer.close();
        };
    }

    /**
     * 配置一个密码加密器
     * 每次加密都会生成不同的加密密码，但是每个不同的加密密码都可以解密成原来的密码
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder(){
        return new BCryptPasswordEncoder();
    }

    public static void main(String[] args) {
        PasswordEncoder passwordEncoder=new BCryptPasswordEncoder();
        String encode = passwordEncoder.encode("123");
        String encode2 = passwordEncoder.encode("123");
        String encode3 = passwordEncoder.encode("123");
        System.out.println("加密：");
        System.out.println(encode);
        System.out.println(encode2);
        System.out.println(encode3);
        System.out.println("解密：");
        boolean matches = passwordEncoder.matches("123", "$2a$10$SRdh2jynSN.PGnGUAGjpI.rzCfCFdHN0AZ1eg8lWfthrRkHWo2bwa");
        boolean matches2 = passwordEncoder.matches("123", "$2a$10$HeBc1mNbuFBPN304fz3g6.Mn6aoi9Cqh9rnqQaEk.bKPoggJD19lG");
        boolean matches3 = passwordEncoder.matches("123", "$2a$10$FVth29kEvOH7NIM9fEecLuq.KCysPuKyaZoyziGD0X89lKfMz1AKe");
        System.out.println(matches);
        System.out.println(matches2);
        System.out.println(matches3);
    }
}
